﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using SpotItComponents;

using Vitruvian.Distribution;
using Vitruvian.Distribution.SyncPatterns;
using Vitruvian.Services;
using Vitruvian.Serialization;
using Vitruvian.Logging;

namespace SpotItServerAPI
{
    [DistributionInfo]
    [OptimisticSerialization]
    public class DistributedManager : DistributedService
    {
        #region Data Types
        protected delegate OperationResult OperationMethod(object[] opParam);
        protected enum SecurityLevel { None, Player, Designer, Admin };
        #endregion

        #region Protected and Private Data Members
        protected Logger logger;
        protected SessionManager sessionManager = null;
        protected object myLock = new object();
        #endregion

        #region Constructor(s)
        public DistributedManager() : base("Unknown Manager") { }
        public DistributedManager(string name) : base(name) { }
        #endregion

        #region IService Members
        public override void Init()
        {
            Load();
        }

        public override void Cleanup()
        {
            Save();
        }
        #endregion

        #region Protected Methods
        protected OperationResult DoOperation(Session currentSession, OperationMethod opMethod, SecurityLevel minimumSecurityLevelAdmin, params object[] opParams)
        {
            OperationResult result;

            logger.Debug("In DoOperation");

            sessionManager = ServiceRegistry.GetPreferredService<SessionManager>();

            if (sessionManager == null)
                result = new OperationResult("A Session Manager could not be found, restart the server process");
            else if ((result = PreCondition()).Success)
            {
                logger.Debug("Past PreCondition");

                if ((result = Authorization(currentSession, minimumSecurityLevelAdmin)).Success)
                {
                    logger.Debug("Past Authorization");

                    // Build an extended parameters list that includes the current session as the first parameter
                    object[] newOpParams = new object[opParams.Length + 1];
                    newOpParams[0] = currentSession;
                    for (int i = 0; i < opParams.Length; i++)
                        newOpParams[i + 1] = opParams[i];

                    logger.Debug("Ready enter the criticial section of DoOperation");

                    lock (myLock)
                    {
                        result = opMethod(newOpParams);
                    }

                    logger.Debug("Exited critical section of DoOperation");
                }
            }

            logger.DebugFormat("Leaving DoOperation, Success={0} {1}", result.Success, result.ErrorMessage);
            return result;
        }

        protected virtual OperationResult PreCondition()
        {
            return new OperationResult();
        }

        protected virtual OperationResult Authorization(Session currentSession, SecurityLevel requiredLevel)
        {
            logger.Debug("In Authorization");
            OperationResult result = new OperationResult();
            Session validatedSession = sessionManager.Validate(currentSession);
            if (!SecurityLevelOkay(validatedSession, requiredLevel))
                result = new OperationResult("Insufficient priviledges for request operation");
            return result;
        }

        protected virtual void Load() {}

        protected virtual void Save() {}
        #endregion

        #region Private Methods

        private bool SecurityLevelOkay(Session currentSession, SecurityLevel requiredLevel)
        {
            bool result = false;

            logger.Debug("In SecurityLevelOkay");

            switch (requiredLevel)
            {
                case SecurityLevel.None:
                    result = true;
                    break;
                case SecurityLevel.Player:
                    result = (currentSession!=null);
                    break;
                case SecurityLevel.Designer:
                    result = (currentSession!=null &&  (currentSession.UserInfo.IsDesigner || currentSession.UserInfo.IsAdmin));
                    break;
                case SecurityLevel.Admin:
                    result = (currentSession!=null && currentSession.UserInfo.IsAdmin);
                    break;
            }
            return result;
        }

        #endregion


    }
}
